package com.jplus.plugin.mybatis;

import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.List;

import javax.sql.DataSource;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jplus.core.anno.boot.Bean;
import com.jplus.core.anno.com.Autowired;
import com.jplus.core.anno.com.Order;
import com.jplus.core.core.ApplicationContext;
import com.jplus.core.core.proxys.Proxy;
import com.jplus.core.core.proxys.ProxyChain;
import com.jplus.core.db.TransactionManager;
import com.jplus.core.utill.clazz.ClassSearchUtil;
import com.jplus.plugin.mybatis.EnableMybatis.MYBATIS;

@com.jplus.core.anno.boot.Configuration
@Order(100)
public class MybatisConfigurationProxy implements Proxy {

	private final Logger logger = LoggerFactory.getLogger(MybatisConfigurationProxy.class);

	private @Autowired ApplicationContext context;

	/**
	 * 设置最后实例化，确保DataSource有值。 如果使用多数据源。请手动创建
	 */
	@Bean
	@Order(Integer.MAX_VALUE)
	public SqlSessionFactory sqlSessionFactory(TransactionManager transactionManager) {
		if (MybatisConfigurationSelector.ENABLE) {
			logger.info("[EnableMybatis] MybatisPlugin starting...");
			DataSource dataSource = TransactionManager.getDynamicDataSource();// 获取：DynamicDataSource
			TransactionFactory transactionFactory = new JdbcTransactionFactory();// 定义事务工厂
			Environment environment = new Environment("ibatisEnvironment", transactionFactory, dataSource);
			Configuration config = new Configuration(environment);
			// config.setCacheEnabled(true); // 启用缓存
			// config.setLazyLoadingEnabled(false);// 延迟加载
			// config.setAggressiveLazyLoading(false);// 延迟加载
			config.setDefaultStatementTimeout(60);// 数据库响应超时时间
			config.addMappers(getProp(context, MYBATIS.DAO_PACKAGE));// 1.配置daopath
			MapperLocations(config, getProp(context, MYBATIS.XML_PATH));// 2.配置xmlMapper
			SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
			return ssf;
		}
		return null;
	}

	@Override
	public Object doProxy(ProxyChain proxyChain) throws Throwable {
		Object ret = null;
		Connection conn = null;
		try {
			conn = TransactionManager.getConnection();
			conn.setAutoCommit(false);
			SqlSession sqlSession = context.getBean(SqlSessionFactory.class).openSession(conn);
			logger.debug("[dao] " + proxyChain.getTargetInterface()[0].getSimpleName() + "."
					+ proxyChain.getTargetMethod().getName());
			Object dao = sqlSession.getMapper(proxyChain.getTargetInterface()[0]);
			// ret = proxyChain.doProxyChain();//抛弃原始对象
			ret = proxyChain.getTargetMethod().invoke(dao, proxyChain.getMethodParams());// 使用新对象，且放弃了代理链，所以Dao的多重代理无效。
			if (!TransactionManager.isTransaction())
				conn.commit();
		} catch (Exception e) {
			e.printStackTrace();
			if (conn != null && !TransactionManager.isTransaction())
				conn.rollback();
			throw e;
		} finally {
			if (conn != null && !TransactionManager.isTransaction())
				conn.close();
		}
		return ret;
	}

	/**
	 * 扫描xmlMapper
	 */
	private void MapperLocations(Configuration config, String xmlPkg) {
		try {
			List<File> xmls = ClassSearchUtil.getXmlFileList(xmlPkg);
			for (File file : xmls) {
				if (!file.exists() && file.getPath().contains("\\test-classes\\"))
					file = new File(file.getPath().replace("\\test-classes\\", "\\classes\\"));
				logger.info("\t>> MapperXml {}", file);
				XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(new FileInputStream(file), config,
						file.toString(), config.getSqlFragments());
				xmlMapperBuilder.parse();
			}
		} catch (Exception e) {
			logger.error("Mybatis MapperLocations Exception:", e);
		}
	}

	public String getProp(ApplicationContext context, MYBATIS key) {
		return context.getEnvironment().getProp(key.getValue());
	}

}
